#include "stdafx.h"

#include "XMLParsers.h"

#include "tinyxml2.h"

using namespace std;
using namespace tinyxml2;

namespace ZeEngine
{
    XMLParsers::XMLParsers(const std::string& iFileName) : m_FileName(iFileName)
                                                          ,m_Loaded(false)
                                                          ,m_pDocument(NULL)
                                                          ,m_pValueMap(NULL)
                                                          ,m_pAttributeMap(NULL)
    {
        LoadDocument();
    }

    XMLParsers::~XMLParsers()
    {
        Utilities::SafeDelete<XMLDocument>(m_pDocument);
        Utilities::SafeDelete<XMLMap>(m_pValueMap);
        Utilities::SafeDelete<XMLMap>(m_pAttributeMap);
    }

    std::string XMLParsers::GetValueString(const std::string& iPath)
    {
        return Search(iPath, *m_pValueMap);
    }

    std::string XMLParsers::GetAttributeString(const std::string& iPath)
    {
        return Search(iPath, *m_pAttributeMap);
    }

    s32 XMLParsers::GetValueInt(const std::string& iPath)
    {
        return GetValue<s32>(iPath);
    }

    s32 XMLParsers::GetAttributeInt(const std::string& iPath)
    {
        return GetAttribute<s32>(iPath);
    }

    f32 XMLParsers::GetValueFloat(const std::string& iPath)
    {
        return GetValue<f32>(iPath);
    }

    f32 XMLParsers::GetAttributeFloat(const std::string& iPath)
    {
        return GetAttribute<f32>(iPath);
    }

    std::string XMLParsers::Search(const std::string& iPath, const XMLMap& iMap)
    {    
        string path = m_Path + iPath;

        int count = iMap.count(path);
        string value = "";

        if (count > 1)
        {
            LOG_WARNING("Found multiple " + path + " in XML file: " + m_FileName);
        }
        else if (count == 0)
        {
            LOG_ERROR("Could not find " + path + " in XML file: " + m_FileName);
        }
        else
        {       
            XMLMap::const_iterator it = iMap.find(path);
            value = it->second;
        }

        assert(value != "");
        return value;
    }

    bool XMLParsers::ValueExists(const std::string& iPath)
    {
        string path = m_Path + iPath;
        return m_pValueMap->count(path) > 0;
    }

    bool XMLParsers::AttributeExists(const std::string& iPath)
    {
        string path = m_Path + iPath;
        return m_pAttributeMap->count(path) > 0;
    }

    XMLNodeList XMLParsers::GetNodeList(const std::string& iPath)
    {
        string path = m_Path + iPath;
        XMLNodeList xmlNodeList;
        m_Root.Search(path, xmlNodeList);
        return xmlNodeList;
    }

    void XMLParsers::LoadDocument()
    {
        m_pValueMap = new XMLMap();
        m_pAttributeMap = new XMLMap();
        m_pDocument = new XMLDocument();

        if (m_pAttributeMap && m_pValueMap)
        {       
            if (m_pDocument)
            {        
                LOG_INFORMATION("Loading XML File... " + m_FileName);

                if (m_pDocument->LoadFile(m_FileName.c_str()) == XML_NO_ERROR)
                {
                    LOG_INFORMATION("Parsing XML File... " + m_FileName);

                    ParseXML(m_pDocument->RootElement());
                    ParseXML(m_pDocument->RootElement(), m_Root);

                    m_Loaded = true;
                }
                else
                {
                    string error1 = m_pDocument->GetErrorStr1();
                    stringstream ss; 
                    ss << m_pDocument->ErrorID();

                    LOG_ERROR("Error loading XML File: " + m_FileName + " Error: " + m_pDocument->GetErrorStr1() + " Error ID: " + ss.str());
                }
            }
        }
    }

    void XMLParsers::ParseXML(XMLElement* pParentElement, std::string iCurrentPath)
    {
        if (pParentElement)
        {
            //Build the current path in the xml up to this point
            if (iCurrentPath == "")
            {
                iCurrentPath = pParentElement->Value();
            }
            else
            {
                iCurrentPath = iCurrentPath + "." + (pParentElement->Value());
            }

            //extract the value inside the tag
            if (pParentElement->GetText())
            {
                string text = pParentElement->GetText();
                m_pValueMap->insert(XMLMapPair(iCurrentPath, text));
            }

            //extract all the attributes inside the tag
            for(const XMLAttribute* pAttribute = pParentElement->FirstAttribute(); pAttribute != NULL; pAttribute = pAttribute->Next())
            {
                string attributeName = pAttribute->Name();
                string attributeValue = pAttribute->Value();
                m_pAttributeMap->insert(XMLMapPair(iCurrentPath + "." + attributeName, attributeValue));
            }

            //recursively parse all the xml document
            for(XMLElement* pElement = pParentElement->FirstChildElement(); pElement != NULL; pElement = pElement->NextSiblingElement())
            {
                ParseXML(pElement, iCurrentPath);
            }
        }
        else
        {
            LOG_ERROR("Error parsing XML File: " + m_FileName);
        }
    }

    void XMLParsers::ParseXML(XMLElement* pParentElement, XMLNode& oNode)
    {
        string name = pParentElement->Value();     

        oNode.SetName(pParentElement->Value());

        //extract the value inside the tag
        if (pParentElement->GetText())
        {
            string text = pParentElement->GetText();
            oNode.SetValue(text);
        }

        //extract all the attributes inside the tag
        for(const XMLAttribute* pAttribute = pParentElement->FirstAttribute(); pAttribute != NULL; pAttribute = pAttribute->Next())
        {
            string attributeName = pAttribute->Name();
            string attributeValue = pAttribute->Value();

            oNode.AddAttribute(attributeName, attributeValue);
        }

        //recursively parse all the xml document
        for(XMLElement* pElement = pParentElement->FirstChildElement(); pElement != NULL; pElement = pElement->NextSiblingElement())
        {
            XMLNode* pNode = oNode.AddChild(name);
            if (pNode)
            { 
                ParseXML(pElement, *pNode);
            }
        }
    }
}